package com.csx.governance.traffic.adapter.outbound.openfeign;

import cn.hutool.core.collection.CollectionUtil;
import com.csx.governance.traffic.adapter.config.TrafficLoadBalancerConfiguration;
import com.csx.governance.traffic.adapter.outbound.LoadBalancerInstanceSupplierBeanPostProcessor;
import com.csx.governance.traffic.adapter.outbound.TrafficServiceInstanceSupplier;
import com.csx.governance.traffic.adapter.outbound.resttemplate.TrafficLoadBalancerInterceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.support.GenericApplicationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p> Feign的请求拦截器 </p>
 * <p>主要作用是向由LoadBalancer创建的子容器中注册自定义的 {@link TrafficServiceInstanceSupplier} (内置自定义的服务实例过滤逻辑)</p>
 * <p>注: 该拦截器继承自 {@link RequestInterceptor} ,因此仅对于 {@link feign.Feign} 发起的外调生效;
 * 针对 {@link org.springframework.web.client.RestTemplate} 发起的外调, 可以参考 {@link TrafficLoadBalancerInterceptor} </p>
 * @author cuisongxu
 * @date 2023/4/12 14:31
 */
public class TrafficServiceInstanceSupplierFeignInterceptor implements RequestInterceptor {

    private final GenericApplicationContext applicationContext;

    public TrafficServiceInstanceSupplierFeignInterceptor(GenericApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void apply(RequestTemplate template) {
        Target<?> target = template.feignTarget();
        String serviceName = target.name();

        registerOutBoundServiceNameBean(serviceName);

        addConfigurationsToLoadBalancerClientFactory();
    }

    /**
     * <p>注册serviceName名称的Bean,类型为 {@link LoadBalancerClientSpecification},主要作用为向LoadBalancer的子容器中注册 {@link TrafficLoadBalancerConfiguration}</p>
     * <p>该类是一个配置类，配置类中注册了一个后置处理器 {@link LoadBalancerInstanceSupplierBeanPostProcessor},负责包装 LoadBalancer子容器中的 {@link ServiceInstanceListSupplier}类型的Bean</p>
     * <p>使得所有 {@link ServiceInstanceListSupplier} 中加入全链路灰度自定义的实例过滤逻辑</p
     */
    private void registerOutBoundServiceNameBean(String serviceName) {
        if(!applicationContext.containsBean(serviceName)) {
            applicationContext.registerBean(serviceName, LoadBalancerClientSpecification.class, () -> new LoadBalancerClientSpecification(serviceName, new Class[]{TrafficLoadBalancerConfiguration.class}));
        }
    }

    /**
     * <p>将容器中所有的 {@link LoadBalancerClientSpecification} 类型的Bean加入到 {@link LoadBalancerClientFactory#setConfigurations(List)}中, 使得在创建LoadBalancer子容器时能够加入
     * {@link LoadBalancerClientSpecification} 中自定义的配置类(这里是加入负责包装{@link ServiceInstanceListSupplier}的后置处理器,添加全链路灰度的实例过滤逻辑)</p>
     */
    private void addConfigurationsToLoadBalancerClientFactory() {
        LoadBalancerClientFactory loadBalancerClientFactory = applicationContext.getBean(LoadBalancerClientFactory.class);

        Map<String, LoadBalancerClientSpecification> loadBalancerClientSpecificationMap = applicationContext.getBeansOfType(LoadBalancerClientSpecification.class);
        List<LoadBalancerClientSpecification> loadBalancerClientSpecificationList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(loadBalancerClientSpecificationMap)) {
            loadBalancerClientSpecificationList = new ArrayList<>(loadBalancerClientSpecificationMap.values());
        }

        loadBalancerClientFactory.setConfigurations(loadBalancerClientSpecificationList);
    }
}
